#include "GRawInputModule.h"
#include "GRawInputDeviceMouse.h"
#include "GRawInputDeviceKeyboard.h"
#include "GMessageManager.h"
#include "GSpinMutex.h"
#include "GScopedLock.h"
#include "GWindow.h"
#include "GDevice.h"
#include "GCore.h"
#include "GError.h"
#include "GLog.h"

#define G_RI_PAGE_GENERIC				1
#define G_RI_PAGE_SIMULATION_CONTROLS	2
#define G_RI_PAGE_VIRTUAL_REALITY		3
#define G_RI_PAGE_SPORT					4
#define G_RI_PAGE_GAME					5
#define G_RI_PAGE_GENERIC_DEVICE		6
#define G_RI_PAGE_KEYBOARD				7
#define G_RI_PAGE_LEDS					8
#define G_RI_PAGE_BUTTON				9

// Valid values for G_RI_PAGE_GENERIC
#define G_RI_PAGE_GENERIC_USAGE_UNDEFINED			0
#define G_RI_PAGE_GENERIC_USAGE_POINTER				1
#define G_RI_PAGE_GENERIC_USAGE_MOUSE				2
#define G_RI_PAGE_GENERIC_USAGE_JOYSTICK			4
#define G_RI_PAGE_GENERIC_USAGE_GAMEPAD				5
#define G_RI_PAGE_GENERIC_USAGE_KEYBOARD			6
#define G_RI_PAGE_GENERIC_USAGE_KEYPAD				7
#define G_RI_PAGE_GENERIC_USAGE_MULTIAXIS_CONTROLS	8
#define G_RI_PAGE_GENERIC_USAGE_TABLET_CONTROLS		9


namespace GRawInput
{

	/************************************************************/
	/*					GRawInputMessageHandler					*/
	/************************************************************/

	GRawInputMessageHandler::GRawInputMessageHandler()
	{
	}

	GRawInputMessageHandler::~GRawInputMessageHandler()
	{
	}

	void GRawInputMessageHandler::SetTargetModule(GRawInputModule* NewModule)
	{
		Module = NewModule;
	}

	GRawInputModule* GRawInputMessageHandler::GetTargetModule() const
	{
		return Module;
	}

	GSSize GRawInputMessageHandler::HandleMessage(const GEngine::GSystemMessage* Message) OVERRIDE PLATFORM
	{
		GUtil::GError::DebugCheck(Module == NULL, "Dont have a valid module.");
		GUtil::GError::DebugCheck(Module == NULL, "Dont have a valid window.");

		switch (Message->Message)
		{
			// There is new input
			case WM_INPUT:
			{
				WPARAM InputCode = GET_RAWINPUT_CODE_WPARAM(Message->Parameter0);
				HRAWINPUT InputHandle = reinterpret_cast<HRAWINPUT>(Message->Parameter1);
				switch (InputCode)
				{
					// When window is in foreground
					case RIM_INPUT:
						Module->HandleInput(InputHandle);
						break;

					// When window is in background
					case RIM_INPUTSINK:
						// Dont care
						break;
				}
			
				break;
			}
			
			// Device must be registered to get this message
			case WM_INPUT_DEVICE_CHANGE:
			{
				HANDLE DeviceHandle = reinterpret_cast<HANDLE>(Message->Parameter1);
				switch (Message->Parameter0)
				{
					// Device found
					case GIDC_ARRIVAL:
						// Dont care arrival for now
						break;
					// Device removed
					case GIDC_REMOVAL:
						// Dont care removal for now
						break;
				}
				break;
			}
		}

		// These messages comes from buffered windows messages
		// so no need to return appropiate windows codes
		return 0;
	}


	/************************************************************/
	/*					GInputModuleRaw							*/
	/************************************************************/

	G_MODULE_DESCRIPTOR(GRawInputModule, GInputModule, GInputModule);
	
	GRawInputModule::GRawInputModule()
	{
		RawMouse = NULL;
		RawKeyboard = NULL;

		MessageHandler = NULL;
	}

	GRawInputModule::~GRawInputModule()
	{
	}

	bool GRawInputModule::HandleInput(HRAWINPUT InputHandle)
	{
		GThread::GScopedLock<GThread::GSpinMutex> Lock(BufferLock);

		// Push RawInput into buffer
		// will be processed at update time
		
		RAWINPUT Buffer;
		UINT Size = sizeof(RAWINPUT);
		UINT WriteSize = GetRawInputData(InputHandle, RID_INPUT, &Buffer, &Size, sizeof(RAWINPUTHEADER));
		if (WriteSize == (UINT)-1)
		{
			GUtil::GError::Error(GUtil::GErrorMessageWin32(GetLastError()));
			return false;
		}

		Lock.Acquire();
		InputBuffer.push_back(Buffer);

		return true;
	}

	bool GRawInputModule::EnumerateDevices()
	{
		// Keyboard and mouse is always present and managed by this module
		
		RawMouse = new GRawInputDeviceMouse(this, NULL);
		RawKeyboard = new GRawInputDeviceKeyboard(this, NULL);

		RegisterDevice("Mouse0", RawMouse);
		RegisterDevice("Keyboard0", RawKeyboard);

		return true;
	}
	
	void GRawInputModule::Reset() OVERRIDE
	{
		GInputModule::Reset();

		GThread::GScopedLock<GThread::GSpinMutex> Lock(BufferLock);

		Lock.Acquire();
		InputBuffer.clear();
	}
	
	bool GRawInputModule::Update(float ElapsedTime) OVERRIDE
	{
		if (!GInputModule::Update(ElapsedTime))
		{
			return false;
		}
		
		// Process messages
		InputArray::iterator End = InputBuffer.end();
		InputArray::iterator Iter = InputBuffer.begin();
		while (Iter != End)
		{
			switch (Iter->header.dwType)
			{
				case RIM_TYPEMOUSE:
					RawMouse->HandleInput(*Iter);
					break;

				case RIM_TYPEKEYBOARD:
					RawKeyboard->HandleInput(*Iter);
					break;

				default:
					break;
			}

			Iter++;
		}

		InputBuffer.clear();

		return true;
	}

	bool GRawInputModule::RegisterDevices()
	{
		// Register possible gaming devices
		RAWINPUTDEVICE InputDevices[2] = {0};

		// Keyboard
		InputDevices[0].usUsagePage = G_RI_PAGE_GENERIC;
		InputDevices[0].usUsage = G_RI_PAGE_GENERIC_USAGE_KEYBOARD;				
		InputDevices[0].dwFlags = RIDEV_DEVNOTIFY;
		InputDevices[0].hwndTarget = NULL; // Get input from focused window

		// Mouse
		InputDevices[1].usUsagePage = G_RI_PAGE_GENERIC;
		InputDevices[1].usUsage = G_RI_PAGE_GENERIC_USAGE_MOUSE;
		InputDevices[1].dwFlags = RIDEV_DEVNOTIFY;
		InputDevices[1].hwndTarget = NULL;

		// Register devices
		BOOL Result = RegisterRawInputDevices(InputDevices, 2, sizeof(RAWINPUTDEVICE));
		if (Result == 0)
		{
			GUtil::GError::Error(GUtil::GErrorMessageWin32(GetLastError()));
			return false;
		}

		return true;
	}

	bool GRawInputModule::Initialize() OVERRIDE
	{
		if (!GInputModule::Initialize())
		{
			return false;
		}

		// Create and set global message handler
		MessageHandler = new GRawInputMessageHandler();
		MessageHandler->SetTargetModule(this);
		MessageHandler->SetTargetWindow(NULL);
		mGMessageManager->AppendHandler(MessageHandler);

		// Reserve memory for Input buffer
		InputBuffer.reserve(G_MAX_RAW_INPUT_COUNT);

		// Register devices to get inputs
		bool Registered = RegisterDevices();
		if (!Registered)
		{
			return false;
		}

		// Create device instances
		bool Enumerated = EnumerateDevices();
		if (!Enumerated)
		{
			return false;
		}

		return true;
	}

	void GRawInputModule::Uninitialize() OVERRIDE
	{
		// No device unregistering method for raw input

		// unregister and delete message handler
		mGMessageManager->RemoveHandler(MessageHandler);	
		G_DELETE(MessageHandler);
	
		InputBuffer.clear();
		
		UnregisterDevice("Mouse0");
		UnregisterDevice("Keyboard0");

		G_DELETE(RawMouse);
		G_DELETE(RawKeyboard);
		
		// Let input manager clean up
		GInputModule::Uninitialize();
	}
}